package com.rw.database;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.slf4j.LoggerFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.rw.rule.DataSourceRule;
import com.rw.rule.SimpleDataSourceRule;

/**
 * 读写分离数据源
 * 
 * @author lkclkc88(lkclkc88@sina.com)
 * @date : 2015年11月23日
 */
public class ReadWriteDataSource implements DataSource {

	private static org.slf4j.Logger log = LoggerFactory.getLogger(ReadWriteDataSource.class);

	private DataSource master;

	private List<DataSource> slaves;
	/**
	 * 数据源规则
	 */
	private DataSourceRule rule;

	private int slaveSize;

	/**
	 * 存放每次调用时，数据源是否可写状态
	 */
	private ThreadLocal<Integer> writer = new ThreadLocal<Integer>();
	/**
	 * 全局可写，默认为true
	 */
	private boolean gloabCanWrite = true;

	public boolean isGloabCanWrite() {
		return gloabCanWrite;
	}

	public void setGloabCanWrite(boolean gloabCanWrite) {
		this.gloabCanWrite = gloabCanWrite;
	}

	/**
	 * 设定数据源当前是否可写，如果是，使用master数据源，否者使用slaves数据源
	 * 
	 * @param write
	 */
	public void setCurrentWrite(boolean write) {
		writer.set(write ? 1 : null);
	}

	public DataSource getMaster() {
		return master;
	}

	public void setMaster(DataSource master) {
		this.master = master;
	}

	public List<DataSource> getSlaves() {
		return slaves;
	}

	public void setSlaves(List<DataSource> slaves) {
		this.slaves = slaves;
	}

	public DataSourceRule getRule() {
		return rule;
	}

	public void setRule(DataSourceRule rule) {
		this.rule = rule;
	}

	/**
	 * 清理
	 */
	private void cleanCurrentWriteStatus() {
		writer.remove();
	}

	public PrintWriter getLogWriter() throws SQLException {
		return master.getLogWriter();
	}

	public void setLogWriter(PrintWriter out) throws SQLException {
		master.setLogWriter(out);

	}

	public void setLoginTimeout(int seconds) throws SQLException {
		master.setLoginTimeout(seconds);

	}

	public int getLoginTimeout() throws SQLException {

		return master.getLoginTimeout();
	}

	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		return master.getParentLogger();
	}

	public <T> T unwrap(Class<T> iface) throws SQLException {
		return master.unwrap(iface);
	}

	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return master.isWrapperFor(iface);
	}

	private DataSource getSlaveDataSource() {
		return rule.process(slaves);
	}

	private Connection getSlaveConnection() throws SQLException {
		if (slaveSize == 0) {
			return master.getConnection();
		} else {
			return getSlaveDataSource().getConnection();
		}

	}

	private Connection getSlaveConnection(String username, String password) throws SQLException {
		if (slaveSize == 0) {
			return master.getConnection(username, password);
		} else {
			return getSlaveDataSource().getConnection(username, password);
		}
	}

	/**
	 * 是否执行写操作
	 * 
	 * @return
	 */
	private boolean isCanWrite() {
		/**
		 * 如果全局设定不能写数据，者直接返回false;
		 */
		if (this.gloabCanWrite) {
			/**
			 * 如果手动设定为写，返回ture
			 */
			if (writer.get() != null) {
				if (log.isDebugEnabled()) {
					log.debug("**************** return master dataSouce******************");
				}
				return true;
			}
			/**
			 * 判断spring的事物，如果事物为只读，返回FALSE，否者返回TRUE
			 */
			boolean read = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
			if (read) {
				if (log.isDebugEnabled()) {
					log.debug("*************** return slave dataSouce*****************");
				}
				return false;
			}
			if (log.isDebugEnabled()) {
				log.debug("**************** return master dataSouce******************");
			}
			return true;
		} else {
			return false;
		}
	}

	public Connection getConnection() throws SQLException {
		try {
			if (isCanWrite()) {
				return master.getConnection();
			} else {
				return getSlaveConnection();
			}
		} finally {
			cleanCurrentWriteStatus();
		}
	}

	public Connection getConnection(String username, String password) throws SQLException {
		try {
			if (isCanWrite()) {
				return master.getConnection(username, password);
			} else {
				return getSlaveConnection(username, password);
			}
		} finally {
			cleanCurrentWriteStatus();
		}
	}

	/**
	 * 初始化线程池，如果线程池能写，则master不能为空。
	 * 
	 * @throws SQLException
	 */
	public void init() throws SQLException {
		if (this.gloabCanWrite) {

			if (null == master) {
				throw new SQLException("master can't be null");
			}
		}
		if (null != slaves) {
			slaveSize = slaves.size();
		}
		if (null == rule) {
			rule = new SimpleDataSourceRule();
			rule.init(slaves);

		}
	}
}
